package com.vinfai.thread.concurrent.locks;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 *
 * 有一个绑定的缓冲区，它支持 put 和 take 方法。
 * 如果试图在空的缓冲区上执行 take 操作，则在某一个项变得可用之前，
 * 线程将一直阻塞；如果试图在满的缓冲区上执行 put 操作，
 * 则在有空间变得可用之前，线程将一直阻塞。
 * 我们喜欢在单独的等待 set 中保存 put 线程和 take 线程，
 * 这样就可以在缓冲区中的项或空间变得可用时利用最佳规划，
 * 一次只通知一个线程。可以使用两个 Condition 实例来做到这一点。 
 * 
 * 1.锁和共享变量概念
 * 2.循环数组概念
 * 可参看ArrayBlockingQueue 实现.
 * @author vinfai
 */
public class BoundedBuffer {
	
	final Lock lock = new ReentrantLock();
	// 表示 "缓冲区非空" 条件  
	final Condition notEmpty = lock.newCondition();
	//表示 "缓冲区没满" 条件  
	final Condition notFull = lock.newCondition();
	
	final Object[] items = new Object[100];
	
	//队列读取索引以及元素个数
	int putIndex,takeIndex,count;
	
	public void put(Object x) {
		
		lock.lock();
		try{
			//if--while 考虑虚假唤醒情况
			
			while(count==items.length){
				notFull.await();
			}
			items[putIndex] = x;
			if(++putIndex==items.length) putIndex = 0;
			++count;
			//非空唤醒
			notEmpty.signal();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}finally{
			lock.unlock();
		}
	}
	
	public Object take(){
		lock.lock();
		Object x = null;
		try{
			while(count==0){
				notEmpty.await();
			}
			 x = items[takeIndex];
			items[takeIndex] = null;
			//++takeIndex;
			if(++takeIndex==items.length) takeIndex = 0;
			--count;
			notFull.signal();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}finally{
			lock.unlock();
		}
		
		return x;
	}
	
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {

	}

}
